package com.yuno.mock.strategy.impl;

import com.yuno.mock.dto.ExcelReadRequest;
import com.yuno.mock.strategy.ShardingStrategy;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 混合分片策略实现
 * 根据数据量和复杂度自动选择最优策略
 * 
 * @author xrm
 * @since 1.0.0
 */
@Component
public class HybridShardingStrategy implements ShardingStrategy {
    
    private static final String STRATEGY_NAME = "HYBRID";
    
    // 阈值配置
    private static final long SMALL_FILE_THRESHOLD = 5000;      // 小文件阈值：5000行
    private static final long MEDIUM_FILE_THRESHOLD = 50000;    // 中等文件阈值：50000行
    private static final long LARGE_FILE_THRESHOLD = 200000;    // 大文件阈值：200000行
    
    @Override
    public List<Shard> createShards(ExcelReadRequest request, long totalRows) {
        long actualRows = calculateActualRows(request, totalRows);
        
        // 根据数据量选择策略
        if (actualRows <= SMALL_FILE_THRESHOLD) {
            return createSmallFileShards(request, totalRows, actualRows);
        } else if (actualRows <= MEDIUM_FILE_THRESHOLD) {
            return createMediumFileShards(request, totalRows, actualRows);
        } else if (actualRows <= LARGE_FILE_THRESHOLD) {
            return createLargeFileShards(request, totalRows, actualRows);
        } else {
            return createVeryLargeFileShards(request, totalRows, actualRows);
        }
    }
    
    @Override
    public String getStrategyName() {
        return STRATEGY_NAME;
    }
    
    @Override
    public boolean supports(String strategyName) {
        return STRATEGY_NAME.equals(strategyName);
    }
    
    /**
     * 小文件分片策略（单分片或少量分片）
     */
    private List<Shard> createSmallFileShards(ExcelReadRequest request, long totalRows, long actualRows) {
        List<Shard> shards = new ArrayList<>();
        
        // 小文件使用单分片或2个分片
        int shardCount = actualRows > 2000 ? 2 : 1;
        
        if (shardCount == 1) {
            // 单分片处理
            String shardId = generateShardId("SMALL", 0);
            long startRow = request.getStartRow();
            long endRow = request.getEndRow() == -1 ? totalRows : Math.min(request.getEndRow(), totalRows);
            
            Shard shard = new Shard(shardId, startRow, endRow, 
                    request.getSheetName(), request.getSheetIndex());
            shard.setPriority(0);
            shard.setEstimatedTimeMs(estimateProcessingTime(actualRows, request, "SMALL"));
            
            shards.add(shard);
        } else {
            // 双分片处理
            long midPoint = request.getStartRow() + actualRows / 2;
            long endRow = request.getEndRow() == -1 ? totalRows : Math.min(request.getEndRow(), totalRows);
            
            // 第一个分片
            Shard shard1 = new Shard(generateShardId("SMALL", 0), 
                    request.getStartRow(), midPoint - 1,
                    request.getSheetName(), request.getSheetIndex());
            shard1.setPriority(0);
            shard1.setEstimatedTimeMs(estimateProcessingTime(midPoint - request.getStartRow(), request, "SMALL"));
            
            // 第二个分片
            Shard shard2 = new Shard(generateShardId("SMALL", 1), 
                    midPoint, endRow,
                    request.getSheetName(), request.getSheetIndex());
            shard2.setPriority(1);
            shard2.setEstimatedTimeMs(estimateProcessingTime(endRow - midPoint + 1, request, "SMALL"));
            
            shards.add(shard1);
            shards.add(shard2);
        }
        
        return shards;
    }
    
    /**
     * 中等文件分片策略（平衡分片）
     */
    private List<Shard> createMediumFileShards(ExcelReadRequest request, long totalRows, long actualRows) {
        List<Shard> shards = new ArrayList<>();
        
        // 中等文件使用4-8个分片
        int optimalShards = calculateOptimalShardsForMedium(actualRows, request);
        long rowsPerShard = actualRows / optimalShards;
        long remainingRows = actualRows % optimalShards;
        
        long currentStartRow = request.getStartRow();
        long actualEndRow = request.getEndRow() == -1 ? totalRows : Math.min(request.getEndRow(), totalRows);
        
        for (int i = 0; i < optimalShards; i++) {
            long shardSize = rowsPerShard;
            if (i < remainingRows) {
                shardSize++;
            }
            
            long shardEndRow = currentStartRow + shardSize - 1;
            if (i == optimalShards - 1) {
                shardEndRow = actualEndRow;
            }
            
            String shardId = generateShardId("MEDIUM", i);
            Shard shard = new Shard(shardId, currentStartRow, shardEndRow,
                    request.getSheetName(), request.getSheetIndex());
            
            shard.setPriority(i);
            shard.setEstimatedTimeMs(estimateProcessingTime(shardSize, request, "MEDIUM"));
            
            shards.add(shard);
            currentStartRow = shardEndRow + 1;
            
            if (currentStartRow > actualEndRow) {
                break;
            }
        }
        
        return shards;
    }
    
    /**
     * 大文件分片策略（高并发分片）
     */
    private List<Shard> createLargeFileShards(ExcelReadRequest request, long totalRows, long actualRows) {
        List<Shard> shards = new ArrayList<>();
        
        // 大文件使用更多分片，但考虑线程池限制
        int maxShards = Math.min(request.getMaxShards(), 16); // 限制最大分片数
        int optimalShards = calculateOptimalShardsForLarge(actualRows, request, maxShards);
        
        // 使用动态分片大小
        List<Long> shardSizes = calculateDynamicShardSizes(actualRows, optimalShards);
        
        long currentStartRow = request.getStartRow();
        long actualEndRow = request.getEndRow() == -1 ? totalRows : Math.min(request.getEndRow(), totalRows);
        
        for (int i = 0; i < shardSizes.size(); i++) {
            long shardSize = shardSizes.get(i);
            long shardEndRow = currentStartRow + shardSize - 1;
            
            if (i == shardSizes.size() - 1) {
                shardEndRow = actualEndRow;
            }
            
            String shardId = generateShardId("LARGE", i);
            Shard shard = new Shard(shardId, currentStartRow, shardEndRow,
                    request.getSheetName(), request.getSheetIndex());
            
            // 大文件使用负载均衡优先级
            shard.setPriority(calculateLoadBalancedPriority(i, shardSize, actualRows));
            shard.setEstimatedTimeMs(estimateProcessingTime(shardSize, request, "LARGE"));
            
            shards.add(shard);
            currentStartRow = shardEndRow + 1;
            
            if (currentStartRow > actualEndRow) {
                break;
            }
        }
        
        return shards;
    }
    
    /**
     * 超大文件分片策略（流式处理）
     */
    private List<Shard> createVeryLargeFileShards(ExcelReadRequest request, long totalRows, long actualRows) {
        List<Shard> shards = new ArrayList<>();
        
        // 超大文件使用固定大小分片，便于流式处理
        int fixedShardSize = Math.min(request.getMaxRowsPerShard(), 20000); // 限制单个分片最大20000行
        int shardCount = (int) Math.ceil((double) actualRows / fixedShardSize);
        
        // 限制分片数量，避免过多分片
        shardCount = Math.min(shardCount, request.getMaxShards());
        
        long currentStartRow = request.getStartRow();
        long actualEndRow = request.getEndRow() == -1 ? totalRows : Math.min(request.getEndRow(), totalRows);
        
        for (int i = 0; i < shardCount; i++) {
            long shardEndRow = Math.min(currentStartRow + fixedShardSize - 1, actualEndRow);
            
            String shardId = generateShardId("XLARGE", i);
            Shard shard = new Shard(shardId, currentStartRow, shardEndRow,
                    request.getSheetName(), request.getSheetIndex());
            
            // 超大文件使用轮询优先级，避免热点
            shard.setPriority(i % 4);
            shard.setEstimatedTimeMs(estimateProcessingTime(shardEndRow - currentStartRow + 1, request, "XLARGE"));
            
            shards.add(shard);
            currentStartRow = shardEndRow + 1;
            
            if (currentStartRow > actualEndRow) {
                break;
            }
        }
        
        return shards;
    }
    
    /**
     * 计算实际需要处理的行数
     */
    private long calculateActualRows(ExcelReadRequest request, long totalRows) {
        long startRow = request.getStartRow();
        long endRow = request.getEndRow() == -1 ? totalRows : Math.min(request.getEndRow(), totalRows);
        return Math.max(0, endRow - startRow + 1);
    }
    
    /**
     * 计算中等文件的最优分片数
     */
    private int calculateOptimalShardsForMedium(long actualRows, ExcelReadRequest request) {
        // 目标：每个分片5000-15000行
        int minShards = (int) Math.ceil((double) actualRows / 15000);
        int maxShards = (int) Math.floor((double) actualRows / 5000);
        
        int optimalShards = (minShards + maxShards) / 2;
        return Math.max(2, Math.min(optimalShards, 8));
    }
    
    /**
     * 计算大文件的最优分片数
     */
    private int calculateOptimalShardsForLarge(long actualRows, ExcelReadRequest request, int maxShards) {
        // 目标：每个分片10000-25000行
        int minShards = (int) Math.ceil((double) actualRows / 25000);
        int maxShardsCalculated = (int) Math.floor((double) actualRows / 10000);
        
        int optimalShards = Math.min(maxShardsCalculated, maxShards);
        return Math.max(minShards, Math.min(optimalShards, maxShards));
    }
    
    /**
     * 计算动态分片大小
     */
    private List<Long> calculateDynamicShardSizes(long totalRows, int shardCount) {
        List<Long> sizes = new ArrayList<>();
        long baseSize = totalRows / shardCount;
        long remainder = totalRows % shardCount;
        
        for (int i = 0; i < shardCount; i++) {
            long size = baseSize;
            if (i < remainder) {
                size++;
            }
            sizes.add(size);
        }
        
        return sizes;
    }
    
    /**
     * 计算负载均衡优先级
     */
    private int calculateLoadBalancedPriority(int shardIndex, long shardSize, long totalRows) {
        // 基于分片大小和位置计算优先级
        double sizeRatio = (double) shardSize / totalRows;
        int basePriority = shardIndex % 4; // 基础轮询优先级
        
        // 较小的分片获得更高优先级
        if (sizeRatio < 0.05) {
            return Math.max(0, basePriority - 1);
        } else if (sizeRatio > 0.15) {
            return Math.min(3, basePriority + 1);
        }
        
        return basePriority;
    }
    
    /**
     * 生成分片ID
     */
    private String generateShardId(String type, int index) {
        return String.format("%s_SHARD_%03d_%s", type, index,
                UUID.randomUUID().toString().substring(0, 8));
    }
    
    /**
     * 估算处理时间
     */
    private long estimateProcessingTime(long rowCount, ExcelReadRequest request, String fileType) {
        // 基础处理时间根据文件类型调整
        long baseTimePerRow = switch (fileType) {
            case "SMALL" -> 3;   // 小文件：每行3毫秒
            case "MEDIUM" -> 5;  // 中等文件：每行5毫秒
            case "LARGE" -> 7;   // 大文件：每行7毫秒
            case "XLARGE" -> 10; // 超大文件：每行10毫秒
            default -> 5;
        };
        
        long baseTime = rowCount * baseTimePerRow;
        
        // 复杂度调整因子
        double complexityFactor = 1.0;
        if (request.getDataTypes() != null && !request.getDataTypes().isEmpty()) {
            complexityFactor *= 1.3;
        }
        if (request.getColumnMapping() != null && !request.getColumnMapping().isEmpty()) {
            complexityFactor *= 1.2;
        }
        if (request.getSkipEmptyRows() || request.getSkipEmptyColumns()) {
            complexityFactor *= 1.1;
        }
        
        return (long) (baseTime * complexityFactor);
    }
}